import urllib.request
import json
import re


class RCell:
    def __init__(self, rowNum: int, colNum: int):
        self.rowNum = rowNum
        self.colNum = colNum

    def __str__(self):
        return f"RCell({self.rowNum}, {self.colNum})"

    def __repr__(self):
        return self.__str__()


class RValue:
    def __init__(self, rowNum: int, colNum: int, value: int):
        self.rowNum = rowNum
        self.colNum = colNum
        self.value = value

    def __str__(self):
        return f"RValue({self.rowNum}, {self.colNum}, {self.value})"

    def __repr__(self):
        return self.__str__()


class RCands:
    def __init__(self, rowNum: int, colNum: int, cands: set):
        self.rowNum = rowNum
        self.colNum = colNum
        self.cands = cands

    def __str__(self):
        return f"RCand({self.rowNum}, {self.colNum}, {self.cands})"

    def __repr__(self):
        return self.__str__()


class RNode:
    def __init__(self, cand):
        self.kind = ""
        self.cand = cand
        self.rcells = []

    def __str__(self):
        return f"RNode({''.join(map(str, self.rcells))}[{self.cand}])"

    def __repr__(self):
        return self.__str__()


class RLink:
    def __init__(self):
        # self.firstChain = firstChain
        self.chains = []
        self.rnodes = []

    def __str__(self):
        tokens = []
        # chain = self.firstChain
        for rnode, chain in zip(self.rnodes[:-1], self.chains):
            tokens.append(str(rnode))
            tokens.append(chain)
            # if chain == "=":
            #     chain = "-"
            # else:
            #     chain = "="

        tokens.append(str(self.rnodes[-1]))

        return "".join(tokens)

    def __repr__(self):
        return self.__str__()


class RHouse:
    def __init__(self, kind: str, num: int):
        self.kind = kind
        self.num = num

    def __str__(self):
        return f"RHouse({self.kind}, {self.num})"

    def __repr__(self):
        return self.__str__()


class RResult:
    def __init__(self):
        self.chosen = None
        self.removed = []
        self.helphouses = []
        self.helpcells = []
        self.helpvalues = []
        self.helpcands = []
        self.helplinks = []
        self.strategyname = ""

    def __str__(self):
        return f"RResult({self.chosen}, {self.removed}, {self.helpcells}, {self.helpvalues}, {self.helpcands}, {self.helplinks}, {self.strategyname})"


def rpc_call(url, method, args):
    data = json.dumps(
        {"jsonrpc": "2.0", "method": method, "params": args, "id": 1}
    ).encode()
    req = urllib.request.Request(
        url,
        data,
        {"Content-Type": "application/json"},
    )
    f = urllib.request.urlopen(req)
    response = f.read()
    resp = json.loads(response)
    return resp


def rpc_check(grid):
    url = "http://localhost:8000"
    method = "check"
    args = grid
    resp = rpc_call(url, method, args)
    return resp
    # eturn unmarshal(resp)


def rpc_strategy():
    url = "http://localhost:8000"
    method = "strategy"
    args = ""
    resp = rpc_call(url, method, args)
    return resp
    # eturn unmarshal(resp)


def rpc_step(strategy, grid):
    url = "http://localhost:8000"
    method = "step"
    args = [strategy, grid]
    resp = rpc_call(url, method, args)

    return resp


def rpc_solve(grid):
    url = "http://localhost:8000"
    method = "solve"
    args = grid
    resp = rpc_call(url, method, args)

    return resp


def rpc_make(grid):
    url = "http://localhost:8000"
    method = "make"
    args = grid
    resp = rpc_call(url, method, args)

    return resp

    # return unmarshal(resp)

    """
        Chosen       []RValue   `json:"chosen"`
        Removed      []RCand    `json:"removed"`
        HelpCells    [][]RCell  `json:"helpcells"`
        HelpValues   [][]RValue `json:"helpvalues"`
        HelpCands    [][]RCand  `json:"helpcands"`
        HelpLinks     [][]RLink  `json:"helplinks"`
        StrategyName string     `json:"strategyname"`
    """


def unmarshal(result):
    """
    error = resp["error"]

    if error:
        print(error)
        return None

    # print("####", resp)
    result = resp["result"]
    print("Result", result)
    """
    # result = json.loads(result)
    rresult = RResult()

    chosen = result["chosen"]
    if chosen:
        chosenPattern = r"R(\d)C(\d)\((\d)\)"
        fs = re.fullmatch(chosenPattern, chosen)
        if fs:
            f = fs.group(1, 2, 3)
            rresult.chosen = RValue(int(f[0]) - 1, int(f[1]) - 1, int(f[2]))

    # print("1.######", chosen)
    removed = result["removed"]
    if removed:
        removedPattern = r"R(\d)C(\d)\[((\d)+)\]"
        for x in removed:
            fs = re.fullmatch(removedPattern, x)
            # print(x, fs)
            if fs:
                f = fs.group(1, 2, 3)
                # print("f", f)
                rresult.removed.append(
                    RCands(int(f[0]) - 1, int(f[1]) - 1, set([int(e) for e in f[2]]))
                )
    # print("2.######", rresult.removed)
    helphouses = result["helphouses"]
    if helphouses:
        helphousesPattern = r"(R|C|B)(\d)"
        for i, xs in enumerate(helphouses):
            rresult.helphouses.append([])
            for x in xs:
                fs = re.fullmatch(helphousesPattern, x)
                # print("fs", fs)
                if fs:
                    f = fs.group(1, 2)
                    rresult.helphouses[i].append(RHouse(f[0], int(f[1]) - 1))

    helpcells = result["helpcells"]
    if helpcells:
        helpcellsPattern = r"R(\d)C(\d)"
        for i, xs in enumerate(helpcells):
            rresult.helpcells.append([])
            for x in xs:
                fs = re.fullmatch(helpcellsPattern, x)
                # print("fs", fs, x)
                if fs:
                    f = fs.group(1, 2)
                    rresult.helpcells[i].append(RCell(int(f[0]) - 1, int(f[1]) - 1))

    helpvalues = result["helpvalues"]
    if helpvalues:
        helpvaluePattern = r"R(\d)C(\d)\((\d)\)"
        for i, xs in enumerate(helpvalues):
            rresult.helpvalues.append([])
            for x in xs:
                fs = re.fullmatch(helpvaluePattern, x)
                # print("fs", fs, x)
                if fs:
                    f = fs.group(1, 2, 3)
                    rresult.helpvalues[i].append(
                        RValue(int(f[0]) - 1, int(f[1]) - 1, int(f[2]))
                    )

    helpcands = result["helpcands"]
    if helpcands:
        helpcandsPattern = r"R(\d)C(\d)\[((\d)+)\]"
        for i, xs in enumerate(helpcands):
            rresult.helpcands.append([])
            for x in xs:
                fs = re.fullmatch(helpcandsPattern, x)
                # print("fs", fs)
                if fs:
                    f = fs.group(1, 2, 3)
                    rresult.helpcands[i].append(
                        RCands(
                            int(f[0]) - 1, int(f[1]) - 1, set([int(e) for e in f[2]])
                        )
                    )

    helplinks = result["helplinks"]
    if helplinks:
        for link in helplinks:
            rresult.helplinks.append(analyzeLink(link))
    rresult.strategyname = result["strategyname"]
    # print("rresult", rresult.chosen, rresult.helphouses, rresult.strategyname)

    return rresult


def analyzeLink(link):
    chains = []
    for c in link:
        if c in ["-", "="]:
            chains.append(c)
    if not chains:
        return None
    rlink = RLink()
    rlink.chains = chains
    nodes = re.split(r"[-=]", link)
    for node in nodes:
        node = analyzeNode(node)
        if node:
            rlink.rnodes.append(node)
            # print(node)
    return rlink


def analyzeNode(node):
    pattern = r"R(\d)C(\d)\[(\d)\]"
    fs = re.fullmatch(pattern, node)
    if fs:
        f = fs.group(1, 2, 3)
        node = RNode(int(f[2]))
        node.kind = "single"
        node.rcells.append(RCell(int(f[0]) - 1, int(f[1]) - 1))

        return node

    pattern = r"R(\d)C(\d+)\[(\d)\]"
    fs = re.fullmatch(pattern, node)
    if fs:
        f = fs.group(1, 2, 3)
        node = RNode(int(f[2]))
        node.kind = "rowgroup"
        for colNum in sorted(f[1]):
            node.rcells.append(RCell(int(f[0]) - 1, int(colNum) - 1))

        return node

    pattern = r"R(\d+)C(\d)\[(\d)\]"
    fs = re.fullmatch(pattern, node)
    if fs:
        f = fs.group(1, 2, 3)
        node = RNode(int(f[2]))
        node.kind = "colgroup"
        for rowNum in sorted(f[0]):
            node.rcells.append(RCell(int(rowNum) - 1, int(f[1]) - 1))

        return node

    pattern = r"B(\d)P(\d+)\[(\d)\]"
    fs = re.fullmatch(pattern, node)
    if fs:
        f = fs.group(1, 2, 3)
        node = RNode(int(f[2]))
        node.kind = "boxgroup"
        box = int(f[0])
        start_row = (box - 1) // 3 * 3
        start_col = (box - 1) % 3 * 3
        for pos in sorted(f[1]):
            row = start_row + (int(pos) - 1) // 3
            col = start_col + (int(pos) - 1) % 3
            node.rcells.append(RCell(row, col))

        return node

    return None


if __name__ == "__main__":
    s = """ 
34(6)[158](9)[58][15]2(7)1[27]5[2348][2478][23478](9)[48]6[78]9[28]6[124578][24578][15][48](3)5(3)97[24][24]86(1)(4)(6)(1)[58]3[58](2)(7)(9)(2)87(9)(6)13(5)4[78][127][248][1248][12478]9(6)3(5)6[17][38][1358][1578][3578]4(9)2(9)5[234][234][24](6)(7)18"
    """
    r = rpc_step(s)
    print("$$$$$$$$$", r.removed)
    # r = unmarshal(r)
    # print(r.removed)
